iT邦幫忙

2022 iThome 鐵人賽

DAY 3
0
Mobile Development

在 iOS 開發路上的大小事2系列 第 3

【在 iOS 開發路上的大小事2-Day03】工人智慧!JSON 轉起來

  • 分享至 

  • xImage
  •  

最近專案需要有轉換 JSON 的功能,所以就去研究一下了

Swift 原生就有 JSON 編碼、解碼的功能
只要讓 class/struct 繼承 Encodable 或是 Decodable 就可以有編碼 或是 解碼的功能
如果是繼承 Codable 的話,則是 Encodable 跟 Decodable 這兩種功能都有

▲圖截自 Xcode 內的 Swift.Misc

那接下來就來開始製作要做成 JSON 的 class

// 先寫沒繼承 Codable 的 class
class Event: NSObject {
    var Index: Int?
    var TimeStamp: Int64?
    var EventID: Int?
    var EventValue: Int?
    var EventAttribute: [String]?
}

↑ 分別寫兩個來做個對比 ↓

// 在寫有繼承 Codable 的 class
class Event: NSObject, Codable {
    var Index: Int?
    var TimeStamp: Int64?
    var EventID: Int?
    var EventValue: Int?
    var EventAttribute: [String]?
}

接著先來寫一下要製作成 JSON 的內容

let event = Event()
event.Index = 0
event.TimeStamp = 1648647707
event.EventID = 2
event.EventValue = 0
event.EventAttribute = ["饅頭", "1", "早餐早餐"]

接著再來寫轉換 func~

/// 轉換 API 上傳的 JSON Format
/// - Parameters:
///   - input: EventTable
/// - Returns: JSON Data
func GetJsonEventReport(input: Event) -> Data? {
    
}

這邊有兩種版本的寫法,一種是對 Encodable 進行 extension,一種是直接寫

// Version 1:對 Encodable 進行 extension
extension Encodable {
    func asDictionary() throws -> [String: Any] {
        let data = try JSONEncoder().encode(self)
        guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) as? [String: Any] else {
            throw NSError()
        }
        return dictionary
    }
}

func GetJsonEventReport(input: Event) -> Data? {
    let dic1 = try? input.asDictionary()
    print("dic1:\n \(dic1)\n")
    let jsonResults = try? JSONSerialization.data(withJSONObject: dic1 ?? [:], options: .prettyPrinted)
    return jsonResults
}
Version 1 輸出結果:
dic1:
 Optional(["EventID": 2, "EventValue": 0, "Index": 0, "TimeStamp": 1648647707, "EventAttribute": <__NSArrayI 0x600002036af0>(
饅頭,
1,
早餐早餐
)
])
// Version 2:直接寫
func GetJsonEventReport(input: Event) -> Data? {
    let dic2: [String: Any] = [
        "CmdType": 2,
        "ID": input.Index,
        "DeviceID": "SK0xE0512444378B",
        "UserID": "leo160918@gmail.com",
        "EventID": input.EventID,
        "EventValue": input.EventValue,
        "EventRecordTime": input.TimeStamp?.timeStampToDate().convert2UtcStr(),
        "EventAttribute": input.EventAttribute,
        "Note": input.EventAttribute?.last
    ]
    print("dic2:\n \(dic2)\n")
    let jsonResults = try? JSONSerialization.data(withJSONObject: dic2 ?? [:], options: .prettyPrinted)
    return jsonResults
}
Version 2 輸出結果:
dic2:
 ["CmdType": 2, "EventRecordTime": Optional("30/03/2022T13:41:47"), "ID": Optional(0), "EventValue": Optional(0), "DeviceID": "SK0xE0512444378B", "Note": Optional("早餐早餐"), "EventID": Optional(2), "EventAttribute": Optional(["饅頭", "1", "早餐早餐"])]

這兩種版本的寫法都各有優缺點~
下面是我想到的啦,可能還有其他的?

Version 1 優點:
● 可以不用手打,用 Code 直接生成 Dictionary
● 承上點,不會有打錯 Key 的問題
Version 1 缺點:
● 彈性比較低
● 承上點,如果有要新增其他 Key 的話,要手動新增
● 承上點,如果要將自動生成的 Dictionary 裡面特定的 Key 更換的話,要先將原先的 Key 移除

Version 2 優點:
● 彈性比較高
● 承上點,如果有要新增其他 Key 的話,可以直接新增
Version 2 缺點:
● 用手打 Key,可能會有打錯 Key 的問題
● Code 不夠簡潔,要逐一給值

然後補充一下 JSONSerialization 這個東東


▲圖截自 Apple Developer Documentation

一般來說只有 NSArray、NSDictionary 這兩種資料結構可以轉成 JSON
但如果設成 fragmentsAllowed (前身是 allowFragments) 的話,就允許將 String、Number、null、Bool 轉成 JSON
像是下面這樣 ↓

guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) as? [String: Any] else {
    throw NSError()
}

另外將輸出格式設為 prettyPrinted 是為了美化格式
像是下面這樣 ↓

let jsonResults = try? JSONSerialization.data(withJSONObject: dic2 ?? [:], options: .prettyPrinted)

如果要看輸出的 JSON 檔的話,可以透過下面的方法

// jsonResults 是透過 Version 2 的 func GetJsonEventReport(input: Event) -> Data? 回傳出來的 Data
if let jsonData = jsonResults, let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
    let pathWithFileName = documentDirectory.appendingPathComponent("Event_JSON")
    do {
        try jsonData.write(to: pathWithFileName)
    } catch {
        // handle error
    }
}

輸出結果如下~

{
  "EventRecordTime" : "30\/03\/2022T13:41:47",
  "ID" : 0,
  "DeviceID" : "SK0xE0512444378B",
  "EventValue" : 0,
  "EventAttribute" : [
    "饅頭",
    "1",
    "早餐早餐"
  ],
  "Note" : "早餐早餐",
  "EventID" : 2,
  "CmdType" : 2
}

參考資料

  1. https://yongpenglovemimi123.gitbook.io/henry/ios/jsonserialization.readingoptions
  2. https://yongpenglovemimi123.gitbook.io/henry/ios/jsonserialization.writingoptions
  3. https://jjeremy-xue.medium.com/swift-%E7%96%91%E5%95%8F-jsonserialization-%E7%9A%84-options-d151a1e66266
  4. https://www.codeleading.com/article/5134462945/
  5. https://developer.apple.com/documentation/foundation/jsonserialization
  6. https://ithelp.ithome.com.tw/articles/10245672

上一篇
【在 iOS 開發路上的大小事2-Day02】多國語系的其他用法
下一篇
【在 iOS 開發路上的大小事2-Day04】App 內的簡易儲存方式,UserDefaults
系列文
在 iOS 開發路上的大小事230
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言